package logs

import (
	"fmt"
	sl "log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"
	// "git.501joy.net/framework/server/interfaces"
	// "git.501joy.net/framework/server/interfaces"
)

const (
	LOG_FATAL = iota
	LOG_ERROR
	LOG_WARN
	LOG_INFO
	LOG_DEBUG
	LOG_TRACE
	LOG_ALL

	FATAL       = "EXIT-FATAL"
	LOG_CONSOLE = "log_console" // 输出到控制台[editor]

	DEFAULT_SKIP = 2
)

var log *mylog

/*
 * 初始化
 */
func init() {
	log = newMylog()

	// default setting
	log.dir = "/log"
	log.file = "svr"
	log.level = LOG_DEBUG
	log.savefile = true
	log.console = true
	if IsBinDir() {
		log.dir = "../log"
	}
	fmt.Println("log path:", log.dir)

	go log.run()
}

func IsBinDir() bool {
	//修正log目录
	exePath, err := os.Executable()
	if err != nil {
		fmt.Println("Error getting executable path:", err)
		return false
	}
	// 获取当前执行文件所在的目录
	exeDir := filepath.Dir(exePath)
	strs := strings.Split(exeDir, "\\")
	for _, str := range strs {
		if str == "bin" {
			return true
		}
	}
	return false
}

func InitExt(dir string, file string, level string, savefile bool, prefix ...string) {
	Init(dir, file, convertLevel(level), savefile, prefix...)
}

func Init(dir string, file string, level int, savefile bool, prefix ...string) {
	//if os.Getenv(LOG_CONSOLE) != "" {
	//	savefile = false
	//}

	log.setDir(dir)
	log.setFile(file)
	log.setLevel(level)
	log.setSavefile(savefile)
	log.setConsole(os.Getenv(LOG_CONSOLE) != "")

	if len(prefix) != 0 {
		log.setPrefix(fmt.Sprintf("[%v] ", prefix[0]))
	}
}

func Fatal(err ...interface{}) {
	if LOG_FATAL > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_FATAL, fmt.Sprint(err...))
}
func Error(err ...interface{}) {
	if LOG_ERROR > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_ERROR, fmt.Sprint(err...))
}

func Warn(war ...interface{}) {
	if LOG_WARN > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_WARN, fmt.Sprint(war...))
}

func Info(info ...interface{}) {
	if LOG_INFO > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_INFO, fmt.Sprint(info...))
}

func Debug(deb ...interface{}) {
	if LOG_DEBUG > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_DEBUG, fmt.Sprint(deb...))
}

func Trace(deb ...interface{}) {
	if LOG_TRACE > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_TRACE, fmt.Sprint(deb...))
}

func Fatalf(format string, v ...interface{}) {
	if LOG_FATAL > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_FATAL, fmt.Sprintf(format, v...))
}

func FatalfExt(skip int, format string, v ...interface{}) {
	if LOG_FATAL > log.level {
		return
	}
	log.write(skip, LOG_FATAL, fmt.Sprintf(format, v...))
}

func Errorf(format string, v ...interface{}) {
	if LOG_ERROR > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_ERROR, fmt.Sprintf(format, v...))
}

func ErrorfExt(skip int, format string, v ...interface{}) {
	if LOG_ERROR > log.level {
		return
	}
	log.write(skip, LOG_ERROR, fmt.Sprintf(format, v...))
}

func Warnf(format string, v ...interface{}) {
	if LOG_WARN > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_WARN, fmt.Sprintf(format, v...))
}

func WarnfExt(skip int, format string, v ...interface{}) {
	if LOG_WARN > log.level {
		return
	}
	log.write(skip, LOG_WARN, fmt.Sprintf(format, v...))
}

func Infof(format string, v ...interface{}) {
	if LOG_INFO > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_INFO, fmt.Sprintf(format, v...))
}

func InfofExt(skip int, format string, v ...interface{}) {
	if LOG_INFO > log.level {
		return
	}
	log.write(skip, LOG_INFO, fmt.Sprintf(format, v...))
}

func Debugf(format string, v ...interface{}){
	if LOG_DEBUG > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_DEBUG, fmt.Sprintf(format, v...))
}

func DebugfExt(skip int, format string, v ...interface{}) {
	if LOG_DEBUG > log.level {
		return
	}
	log.write(skip, LOG_DEBUG, fmt.Sprintf(format, v...))
}

func Tracef(format string, v ...interface{}) {
	if LOG_TRACE > log.level {
		return
	}
	log.write(DEFAULT_SKIP, LOG_TRACE, fmt.Sprintf(format, v...))
}

func TracefExt(skip int, format string, v ...interface{}) {
	if LOG_TRACE > log.level {
		return
	}
	log.write(skip, LOG_TRACE, fmt.Sprintf(format, v...))
}

/*
func logFormat(u interfaces.IUser, format string, v ...interface{}) (string, []interface{}) {
	if u == nil {
		return "UID[nil] " + format, v
	}
	var vv []interface{}
	vv = append(vv, u.ID())
	vv = append(vv, u.GameKey())
	vv = append(vv, u.Channel())
	vv = append(vv, u.Version())
	vv = append(vv, u.Language())
	vv = append(vv, v...)
	return "UID[%v|%v|%v|%v|%v] " + format, vv
}

func logUIDFormat(id int64, format string, v ...interface{}) (string, []interface{}) {
	var vv []interface{}
	vv = append(vv, id)
	vv = append(vv, v...)
	return "UID[%v] " + format, vv
}

func NewErrorf(u interfaces.IUser, format string, v ...interface{}) error {
	f, vv := logFormat(u, format, v...)
	return fmt.Errorf(f, vv...)
}

func UserDebugf(u interfaces.IUser, format string, v ...interface{}) {
	if LOG_DEBUG > log.level {
		return
	}
	f, vv := logFormat(u, format, v...)
	DebugfExt(3, f, vv...)
}

func UIDDebugf(uid int64, format string, v ...interface{}) {
	if LOG_DEBUG > log.level {
		return
	}
	f, vv := logUIDFormat(uid, format, v...)
	DebugfExt(3, f, vv...)
}
func UserInfof(u interfaces.IUser, format string, v ...interface{}) {
	if LOG_INFO > log.level {
		return
	}
	f, vv := logFormat(u, format, v...)
	InfofExt(3, f, vv...)
}

func UIDInfof(uid int64, format string, v ...interface{}) {
	if LOG_INFO > log.level {
		return
	}
	f, vv := logUIDFormat(uid, format, v...)
	InfofExt(3, f, vv...)
}
func UserWarnf(u interfaces.IUser, format string, v ...interface{}) {
	if LOG_WARN > log.level {
		return
	}
	f, vv := logFormat(u, format, v...)
	WarnfExt(3, f, vv...)
}

func UIDWarnf(uid int64, format string, v ...interface{}) {
	if LOG_WARN > log.level {
		return
	}
	f, vv := logUIDFormat(uid, format, v...)
	WarnfExt(3, f, vv...)
}

func UserErrorf(u interfaces.IUser, format string, v ...interface{}) {
	if LOG_ERROR > log.level {
		return
	}
	f, vv := logFormat(u, format, v...)
	ErrorfExt(3, f, vv...)
}

func UIDErrorf(uid int64, format string, v ...interface{}) {
	if LOG_ERROR > log.level {
		return
	}
	f, vv := logUIDFormat(uid, format, v...)
	ErrorfExt(3, f, vv...)
}

func UserFatalf(u interfaces.IUser, format string, v ...interface{}) {
	if LOG_FATAL > log.level {
		return
	}
	f, vv := logFormat(u, format, v...)
	FatalfExt(3, f, vv...)
}
*/
/*
 * 日志执行函数
 */
type mylog struct {
	log      chan string // 日志chan
	dir      string      // 日志存放目录
	file     string      // 日志文件名
	savefile bool        // 是否保存到文件
	console  bool        // 控制台
	level    int         // 日志级别
	prefix   string      // 日志前缀
}

func newMylog() *mylog {
	log := &mylog{}

	log.log = make(chan string, 100)
	log.dir = "/opt/logs"
	log.file = "out"
	log.savefile = false
	log.console = false
	log.prefix = ""
	return log
}

func (l *mylog) setDir(dir string) {
	l.dir = dir
}

func (l *mylog) setFile(file string) {
	l.file = file
}

func (l *mylog) setSavefile(b bool) {
	l.savefile = b
}

func (l *mylog) setConsole(b bool) {
	l.console = b
}
func (l *mylog) setLevel(level int) {
	l.level = level
}

func (l *mylog) setPrefix(prefix string) {
	l.prefix = prefix
}

func (l *mylog) getLevelString(level int) string {
	switch level {
	case LOG_FATAL:
		return "FAT"
	case LOG_ERROR:
		return "ERR"
	case LOG_WARN:
		return "WRN"
	case LOG_INFO:
		return "INF"
	case LOG_DEBUG:
		return "DBG"
	case LOG_TRACE:
		return "TRC"
	}

	return "unknown"
}

func WriteDirect(str string) {
	str += "\r\n"
	// fmt.Print(str)
	log.log <- str
}

func (l *mylog) write(skip, level int, str string) {
	// 输出日志
	pc, _, line, _ := runtime.Caller(skip)
	p := runtime.FuncForPC(pc)
	t := time.Now().Local().Format("2006/01/02 15:04:05.9999")
	str = fmt.Sprintf("%s%-26s [%s] %s(%d): %s\n", l.prefix, t, l.getLevelString(level), p.Name(), line, str)
	/**
	// 输出到控制台
	if false == l.savefile {
		fmt.Print(str)

		// fatal
		if level == LOG_FATAL {
			os.Exit(1)
		}
		return
	}
	/**/
	if l.console {
		if level == LOG_ERROR {
			sl.Print(str)
		} else {
			fmt.Print(str)
		}
	}

	// 输出到文件
	l.log <- str
	if level == LOG_FATAL {
		l.log <- FATAL
	}
}

func (l *mylog) run() {
	var last, now time.Time

	var fp *os.File

	for {
		str := <-l.log

		if str == FATAL {
			// save
			if fp != nil {
				//fp.Sync()
				fp.Close()
			}
			os.Exit(1)
		}
		// 获取时间
		now = time.Now()

		if (now.Day() != last.Day()) || (fp == nil) { // 跨天或之前文件打开失败,则重新创建文件
			if fp != nil {
				fp.Close()
			}

			// 判断文件夹是否存在
			_, err := os.Stat(l.dir)
			if nil != err {
				os.MkdirAll(l.dir, os.ModePerm)
			}

			path := fmt.Sprintf("%s/%s-%04d-%02d-%02d.log", l.dir, l.file,
				now.Year(), now.Month(), now.Day())
			fp, _ = os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm)
		}

		if fp != nil {
			fp.WriteString(str)
		}
		last = now
	}
}

func convertLevel(level string) int {
	switch level {
	case "fatal", "FATAL":
		return LOG_FATAL
	case "error", "ERROR":
		return LOG_ERROR
	case "warn", "WARN":
		return LOG_WARN
	case "info", "INFO":
		return LOG_INFO
	case "debug", "DEBUG":
		return LOG_DEBUG
	case "trace", "TRACE":
		return LOG_TRACE
	default:
		return LOG_ALL
	}
}
